#coding=utf-8
# Copyright 2011 Kirill Gagarski <gagarin.gtn@gmail.com>
# Copyright 2012-2013 Maxim Petrov <maximpetrov@yahoo.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 3 or any later version.
from panel import Panel
from commit_window import CommitWindow
from controls import Pad
from parse_cmd import parse_cmd

import curses
from mercurial import dispatch, util, ui
import sys
import os
import time
import traceback
import threading
import shlex
import re
import string

class ConsolePanel(Panel):

    """Класс панели с консолью"""
    def __init__(self, main, main_window, resize_function, workdir=os.getcwd()):
        """
        Конструктор класса.

        Аргументы:
        main - объект класса MainWindow, который использует панель
        main_window - __window из объекта класса MainWindow. Нужен для
        перемещения курсора
        resize_function - функция изменения размера главного окна. Нужна для
        повторной отрисовки
        окна после запуска внешних программ в том же терминале (например
        текстовый редактор для commit_message)
        """

        # Позиция курсора относительно набранной команды в консоли.
        # Нулевое значение соответствует положению сразу после prompt'а
        # или последней напечатанной строки
        self.__cursor_position = 0
        # Является ли панель активной в данный момент
        self.__is_active = False
        # Буфер ввода
        self.__inp_buf = u''
        # Сохранённый буфер ввода. Используется при прокрутке истории команд
        # Нажали "вверх" - в __inp_buf - предыдущая команда, а в
        # __inp_buf_saved набранная, но ещё не выполненная
        # При нажатии "вниз" __inp_buf востанавливается из __inp_buf_saved
        self.__inp_buf_saved = u''
        # Полная история консоли. Используется при перерисовке
        self.__history = u''
        # История набранных команд
        self.__cmd_history = []
        self.__cmd_history_position = 0
        # Включено ли эхо в консоли (т. е. печатаются
        # ли набранные с клавиатуры символы). Эхо можно выключать например при
        # вводе пароля
        self.__echo_enabled = True
        # Позиция прокрутки
        self.__scroll_position = 0
        # Блокировка для потокобезопасной записи
        self.__write_lock = threading.RLock()
        # Блокировка, устанавливамая во время выполнения команды
        self.__cmd_running_lock = threading.Lock()
        # Событие, которого ожидает метод read() (без параметров).
        self.__eof_event = threading.Event()
        # Собтытие, которого ожидает метод readline()
        self.__newline_event = threading.Event()
        # Счётчик прочитанных символов после вызова read()
        self.__read_counter = 0
        # Блокировка для счётчика прочитанных символов
        self.__read_counter_lock = threading.Lock()
        # Событие, которого ожидает метод read(N)
        self.__read_counter_exceeded_event = threading.Event()
        #Блокировка чтения
        self.__read_lock = threading.Lock()
        """Строка приглашения консоли"""
        self.prompt = u'hg$ '
        """Рабочий каталог"""
        self.__workdir = main.str_to_internal(workdir)
        # WorkdirPanel из которого берётся текущий каталог для команд hg
        self.workdir_panel = None
        # Объект класса MainWindow
        self.__main = main
        # Главное окно (curses.window) (__window из MainWindow)
        self.__main_window = main_window
        # Функция изменения размера главного окна. Нужна для повторной отрисовки
        # окна после запуска внешних программ в том же терминале (например текстовый
        # редактор для commit_message)
        self.__resize_main_window_function = resize_function
        self.__y, self.__x = 1,1
        # Ширина и высота панели
        self.__height, self.__width = 1,1
        self.__pad = Pad(main, lambda w, h: (0, 0, w, h), self.__get_content)
        """
        Функция предварительной обработки команды. Может изменяться извне.
        Вызывает обработчик по-умолчанию, если обработчик извне не задан
        Аргументы:
        command - команда

        Вовзращаемое значение:
        Список аргументов, передаваемых в mercurial.dispatch.dispatch()
        """
        self.preprocess_cmd = self.__default_cmd_preprocess
        """
        Функция пост-обработки команды. Может изменяться извне.
        Вызывает обработчик по-умолчанию, если обработчик извне не задан
        Аргументы:
        command - список аргументов, передаваемых в mercurial.dispatch.dispatch()
        """
        self.postprocess_cmd = self.__default_cmd_postprocess
        self.__selected_regex = re.compile(r"\$\@")
        #self.__scrolldown()
        self.__ui = ConsolePanelMercurialUI()
        self.__ui.fin = self
        self.__ui.fout = self
        self.__ui.ferr = self
        self.__ui.console_panel = self
        self.__ui.main_window = self.__main_window
        self.__ui.main = self.__main
    def __del__(self):
        """
        Деструктор. Ожидает завершения выполнения команды
        """
        #Bug: деструктор пока почему-то не вызывается
        self.wait_for_finish()

    def __redraw_pad(self):
        """
        Функция перерисовки Pad'a
        """

        if self.__is_active and self.visible:
            self.__pad.draw()
            lines = self.__get_content_lines()
            pad_height = len(lines)
            pos_from_end = len(self.__inp_buf) - self.__cursor_position
            x = len(lines[-1])
            y = pad_height - self.__pad.y_shift - 1
            while pos_from_end > x:
                pos_from_end -= x + 1
                x = self.__width - 1
                y -= 1
            x -= pos_from_end
            if y < self.__height:
                self.__main_window.move(self.__pad.y + y, self.__pad.x + x)

    def __get_content_lines(self):
        """
        Вспомогательная функция. Вовзращает строки содержимого Pad'a, перенесенные в соответствии с шириной Pad'a

        Возвращаемое значение:
            Возвращает строки содержимого с учетом переносов
        """

        long_long_line = self.__history
        inp_buf = u""
        if self.__echo_enabled:
            inp_buf = self.__inp_buf
        if self.__cmd_running_lock.acquire(False):
            self.__cmd_running_lock.release()
            long_long_line += self.prompt + inp_buf
        else:
            long_long_line += inp_buf
        long_lines = long_long_line.splitlines()
        lines = []
        for ll in long_lines:
            if len(ll) > 0:
                lines.extend((ll[i:i+self.__width] for i in range(0, len(ll), self.__width)))
            else:
                lines.append(ll)
        if len(lines) > 0 and len(lines[-1]) == self.__width:
            lines.append(u'')
        return lines

    def __get_content(self, s, c):
        """
        Функция отрисовки содержимого консоли

        Аргументы:
            s - номер первой отображаемой строки
            c - количество отображаемых строк

        Возвращаемое значение:
            Возвращает список кортежей (строка, None, None)
        """

        lines = self.__get_content_lines()
        for l in lines[s:s+c]:
            yield (l, None, None)


    def redraw(self, window):
        """
        Функция перемещения и изменения размера

        Аргументы:
        sw - новое subwindow, на котором размещена панель
        """
        self.__subwindow = window
        if not self.visible:
            return
        self.__write_lock.acquire()
        self.__y, self.__x = self.__subwindow.getbegyx()
        self.__height, self.__width = self.__subwindow.getmaxyx()
        self.__pad.draw(self.__x, self.__y, self.__width, self.__height)
        if self.__is_active:
            self.__scrolldown()

        self.__write_lock.release()

    def handle_keypress(self, ch, uni):
        """
        Обработка нажатия клавиши с поддержкой Unicode.

        Аргументы:
        ch - код символа
        uni - True, если ch - является символом Unicode, False - если нет
        (например, является кодом клавиши не являющейся буквой)
        """
        # При нажатии Enter
        if ch == ord('\n'):
            if self.__cmd_running_lock.acquire(False):
                if self.__echo_enabled:
                    command = self.__inp_buf
                    self.__history += self.prompt + self.__inp_buf
                    if self.__inp_buf != "":
                        self.__cmd_history.append(self.__inp_buf)
                    self.__cmd_history_position = len(self.__cmd_history)
                self.__put_cursor(0)
                self.__inp_buf = u''
                self.write("\n")
                thread = RunOneFunctionThread(self.__handle_cmd,
                  [command])
                self.__cmd_running_lock.release()
                thread.start()

            else:
                self.__newline_event.set()
            # Устанавливаем курсор после приглашения


        # При вводе символов
        elif curses.ascii.isprint(ch) or uni:
            self.__read_counter_lock.acquire()
            self.__read_counter -=1
            self.__read_counter_lock.release()
            self.__write_lock.acquire()
            self.__inp_buf = self.__inp_buf[:self.__cursor_position] + \
              unichr(ch) + self.__inp_buf[self.__cursor_position:]
            self.__update_command_line(1)
            self.__redraw_pad()
            self.__write_lock.release()
        # Backspace
        elif ch == curses.KEY_BACKSPACE:
            self.__read_counter_lock.acquire()
            self.__read_counter +=1
            self.__read_counter_lock.release()
            self.__write_lock.acquire()
            if self.__cursor_position > 0:
                self.__inp_buf = \
                  self.__inp_buf[:self.__cursor_position - 1] + \
                  self.__inp_buf[self.__cursor_position:]
                if self.__echo_enabled:
                    self.__update_command_line(-1)
            self.__write_lock.release()
        # Стрелка влево
        elif ch == curses.KEY_LEFT:
            self.__write_lock.acquire()
            if self.__echo_enabled:
                if self.__cursor_position > 0:
                    self.__put_cursor(self.__cursor_position - 1)
            else:
                self.__cursor_position = self.__cursor_position - 1
            self.__write_lock.release()
        # Стрелка вправо
        elif ch == curses.KEY_RIGHT:
            self.__write_lock.acquire()
            if self.__echo_enabled:
                if self.__cursor_position < len(self.__inp_buf):
                    self.__put_cursor(self.__cursor_position + 1)
            else:
                self.__cursor_position = self.__cursor_position + 1
            self.__write_lock.release()
        # Delete
        elif ch == curses.KEY_DC:
            self.__read_counter_lock.acquire()
            self.__read_counter +=1
            self.__read_counter_lock.release()
            self.__write_lock.acquire()
            if self.__cursor_position < len(self.__inp_buf):
                self.__inp_buf = self.__inp_buf[:self.__cursor_position] + \
                  self.__inp_buf[self.__cursor_position + 1:]
                if self.__echo_enabled:
                    self.__update_command_line(0)
            self.__write_lock.release()
        #Page up
        elif ch == curses.KEY_PPAGE:
            #raise Exception([self.__history])
            self.__write_lock.acquire()
            position = self.__scroll_position - self.__height
            self.__scrollto(position)
            self.__write_lock.release()
        #Page down
        elif ch == curses.KEY_NPAGE:
            self.__write_lock.acquire()
            position = self.__scroll_position + self.__height
            self.__scrollto(position)
            self.__write_lock.release()
        # Стрелка вверх
        elif ch == curses.KEY_UP:
            if self.__cmd_running_lock.acquire(False):
                self.__write_lock.acquire()
                if self.__cmd_history_position == 0:
                    self.__write_lock.release()
                    self.__cmd_running_lock.release()
                    return
                if self.__cmd_history_position == len(self.__cmd_history):
                    self.__inp_buf_saved = self.__inp_buf
                self.__cmd_history_position -= 1
                self.__inp_buf = self.__cmd_history[self.__cmd_history_position]
                self.__cursor_position = len(self.__inp_buf)
                self.__cmd_running_lock.release()
                self.__update_command_line(0)
                self.__write_lock.release()
        # Стрелка вниз
        elif ch == curses.KEY_DOWN:
          if self.__cmd_running_lock.acquire(False):
                self.__write_lock.acquire()
                if self.__cmd_history_position == len(self.__cmd_history):
                    self.__write_lock.release()
                    self.__cmd_running_lock.release()
                    return
                self.__cmd_history_position += 1
                if self.__cmd_history_position == len(self.__cmd_history):
                    self.__inp_buf = self.__inp_buf_saved
                else:
                    self.__inp_buf = self.__cmd_history[self.__cmd_history_position]
                self.__cursor_position = len(self.__inp_buf)
                self.__cmd_running_lock.release()
                self.__update_command_line(0)
                self.__write_lock.release()
        # Home
        elif ch == curses.KEY_HOME:
            self.__put_cursor(0)
        # End
        elif ch == curses.KEY_END:
            self.__put_cursor(len(self.__inp_buf))
        self.__read_counter_lock.acquire()
        ctr = self.__read_counter
        self.__read_counter_lock.release()
        if ctr == 0:
            self.__read_counter_exceeded_event.set()

    def set_active(self):
        """
        Делает панель активной
        """
        self.__is_active = True
        self.__scrollto(self.__scroll_position)


    def set_inactive(self):
        """
        Делает панель неактивной
        """
        self.__is_active = False
        curses.curs_set(0)


    def __put_cursor(self, position):
        """
        Помещает курсор на позицию position

        Аргументы:
        position - позиция
        """
        self.__write_lock.acquire()
        self.__cursor_position = position
        if self.__is_active:
            curses.curs_set(1)
            self.__main_window.refresh()
            self.__subwindow.refresh()
            self.__scrolldown()
        self.__write_lock.release()


    def __handle_cmd(self, command):
        """
        Обрабатывает команду

        command - команда
        """
        self.__cmd_running_lock.acquire()
        command = self.__main.str_to_external(command)
        if command == "":
            pass
        else:
            old_workdir = os.getcwd()
            cmd = self.preprocess_cmd(command)
            if cmd:
                try:
                    if self.workdir_panel is not None:
                        full_path = self.__workdir + "/" + \
                          self.workdir_panel.get_curpath(False)
                        if full_path[-1] == "/":
                            path_spl = full_path.rsplit("/", 2)
                        else:
                            path_spl = full_path.rsplit("/", 1)
                        os.chdir(self.__main.str_to_external(path_spl[0], is_filename=True))
                    else:
                        os.chdir(self.__main.str_to_external(self.__workdir, is_filename=True))
                    if command == "pwd":
                        print >>self, os.getcwd()
                    else:
                        dispatch.dispatch(dispatch.request(cmd, self.__ui))
                except Exception:
                    traceback.print_exc(file=self)
                self.postprocess_cmd(cmd)
                os.chdir(old_workdir)
        self.__cmd_running_lock.release()
        self.__scrolldown()
        self.__redraw_pad()

    def __update_command_line(self, cursor_delta):
        """
        Обновляет строку с набранной командой

        Аргументы:
        cursor_delta - сдвиг курсора (положительный или отрицательный)
        """
        self.__write_lock.acquire()
        self.__redraw_pad()
        self.__put_cursor(self.__cursor_position + cursor_delta)
        self.__write_lock.release()

    def __scrollto(self, position):
        """
        Перематывает консоль на позицию position

        Аргументы:
        position - строка консоли, которая находится на нижней строке панели
        """
        self.__write_lock.acquire()
        pad_height = len(self.__get_content_lines())
        if(position >= pad_height - self.__height):
            self.__pad.y_shift = self.__scroll_position = max(0, pad_height - self.__height)
            self.__put_cursor(self.__cursor_position)
            curses.curs_set(1)
        elif position <= 0:
            curses.curs_set(0)
            self.__pad.y_shift = self.__scroll_position = 0
        else:
            curses.curs_set(0)
            self.__pad.y_shift = self.__scroll_position = position
        self.__redraw_pad()
        self.__write_lock.release()

    def __scrolldown(self):
        """
        Перематывает консоль в самый низ
        """
        self.__write_lock.acquire()
        pad_height = len(self.__get_content_lines())
        self.__pad.y_shift = self.__scroll_position = max(0, pad_height - self.__height)
        if self.__is_active:
            curses.curs_set(1)
        self.__redraw_pad()
        self.__write_lock.release()

    def __default_cmd_preprocess(self, command):
        """
        Предварительная обработка команды перед отправкой её функции
        mercurial.dispatch.dispatch()
        (Функция по умолчанию)
        Аргументы:
        command - команда

        Вовзращаемое значение:
        Список аргументов, передаваемых в mercurial.dispatch.dispatch()
        """
        self.__write_lock.acquire()
        #Если в строке присутствует $@, то формируем замену
        #TODO: Придумать как экранировать
        if self.__selected_regex.search(command):
            toreplace_list = []
            selected = self.workdir_panel.get_selected_files()
            for s in selected:
                toreplace_list.append("\"")
                toreplace_list.append("/")
                toreplace_list.append(self.__main.str_to_external(s))
                toreplace_list.append("\" ")
            command = self.__selected_regex.sub("".join(toreplace_list), command)
        try:
            cmd = shlex.split(command)
        except ValueError:
            self.__write_lock.release()
            print >>self, _("Command preprocessing: syntax error")
            return []
        if not cmd:
            self.__write_lock.release()
            return cmd
        #Если команда начинается с "hg", то удаляем "hg"
        if cmd[0] == "hg":
            cmd = cmd[1:]
        try:
            cmd_name, func, args, options, cmdoptions = \
              parse_cmd(cmd, self.__ui)

        except Exception:
            self.__write_lock.release()
            return cmd
        
        if cmd_name == "merge":
            self.__main.hide()
            self.__ui.fout = sys.stdout
            self.__ui.ferr = sys.stderr
            self.__ui.fin = sys.stdin
        self.__write_lock.release()
        if cmd_name == "commit" and not args:
            args = []
            args.insert(1, ".")
        if cmd_name == "commit" and 'message' in cmdoptions:
            self.__ui.commit_message = cmdoptions['message']
            cmdoptions['message'] = ''
        mod_args = []
        for arg in args:
            if arg[0] == '/':
                to_add = string.replace(arg, "/", self.__main.repo_util.workdir + "/", 1)
                mod_args.append(to_add)
            else:
                mod_args.append(arg)
        #Востанавливаем препроцессированную команду
        new_cmd = []
        for key in options.keys():
            if options[key] is None or not options[key] or (options[key] == ''):
                pass
            elif options[key] == True:
                new_cmd.append("--" + key.replace("_", "-"))
            elif type(options[key]) == list:
                for opt in options[key]:
                    new_cmd.append("--" + key.replace("_", "-"))
                    new_cmd.append(opt)
            else:
                new_cmd.append("--" + key.replace("_", "-"))
                new_cmd.append(options[key])
        new_cmd.append(cmd_name)
        for key in cmdoptions.keys():
            if cmdoptions[key] is None or not cmdoptions[key] or (cmdoptions[key] == ''):
                pass
            elif cmdoptions[key] == True:
                new_cmd.append("--" + key.replace("_", "-"))
            elif type(cmdoptions[key]) == list:
                for opt in cmdoptions[key]:
                    new_cmd.append("--" + key.replace("_", "-"))
                    new_cmd.append(opt)
            else:
                new_cmd.append("--" + key.replace("_", "-"))
                new_cmd.append(cmdoptions[key])
        for arg in mod_args:
            new_cmd.append(arg)
        return new_cmd

    def __default_cmd_postprocess(self, cmd):
        """
        Пост-обработка команды
        mercurial.dispatch.dispatch()
        (функция по умолчанию)
        Аргументы:
        command - Список аргументов, передаваемых в mercurial.dispatch.dispatch()
        """
        #cmd = command.split()
        self.__write_lock.acquire()
        try:
            cmd_name, func, args, options, cmdoptions = parse_cmd(cmd, self.__ui)
        except Exception:
            self.__write_lock.release()
            return
        if cmd_name == "merge":
            self.__ui.flush()
            sys.stdout.write(_("Press any key to continue..."))
            sys.stdout.flush()
            sys.stdin.read(1)
            self.__ui.fout = self
            self.__ui.ferr = self
            self.__ui.fin = self
            self.__main.unhide()

        self.__write_lock.release()

    def wait_for_finish(self):
        """
        Ожидать завершения выполнения команды
        """
        self.__cmd_running_lock.acquire()
        self.__cmd_running_lock.release()

    def read_password(self):
        """
        Прочитать пароль с консоли (readline) с выключением эха.
        """
        self.__echo_enabled = False
        passw = self.readline()
        self.__echo_enabled = True
        return passw

    def set_echo(self, echo):
        """
        Включение/выключение эха в консоли

        Аргументы:
        echo - True - если включени, False - если выключено
        """
        self.__write_lock.acquire()
        self.__echo_enabled = echo
        self.__write_lock.release()

    def is_input_buffer_empty(self):
        """
        Пуст ли буфер ввода?

        Возвращаемое значение:
        True - если пуст
        False - если не пуст
        """
        if self.__inp_buf == u"":
            return True
        else:
            return False

#-----------Методы, свойства и атрибуты имплементирующие file-like object-------
    def __get_new_lines(self):
        return '\n'
    """Символ перевода строки"""
    newlines = property(__get_new_lines)

    """
    Атрибут для управления пробелами оператора print
    Подробнее: http://docs.python.org/library/stdtypes.html#bltin-file-objects
    """
    softspace = 0

    def close(self):
        """
        Закрытие потока (не реализовано)
        """
        raise IOError("Not implemented")

    def flush(self):
        """
        Опустошает буфер выходного потока.
        Поскольку выходной поток не буферизуется, то не делает ничего
        (но метод нужен для реализации file-like object)
        """
        pass

    def fileno(self):
        """
        Вовзращает целочисленный дескриптор файла. не реализовано
        """
        raise IOError("Not implemented")

    def isatty(self):
        """
        Является ли объект терминалом.
        На самом деле не является, но поскольку mercurial проверяет это
        даже там, где в этом нет необходимости, то "обманывает" и вовзращает True
        """
        return True

    def next(self):
        """
        Метод для итерации по строкам. Не реализован, так как в mercurial не
        вызывается
        """
        raise IOError("Not implemented")

    def read(self, size=None):
        """
        Читает size байт из входного потока. Если size=None или аргумент
        отстутствует, то читает, пока не встретит EOF (в данной панели он его не
        встретит никогда)

        Аргументы:
        size - число читаемых байт (по умолчанию -  None)
        """
        self.__read_lock.acquire()
        self.__write_lock.acquire()
        self.__inp_buf = u''
        self.__put_cursor(0)
        self.__write_lock.release()
        if size is None:
            self.__eof_event.clear()
            self.__eof_event.wait()
        elif size == 0:
            return ''
        elif size < 0:
            raise IOError("You cant read negative number of characters")
        else:
            self.__read_counter_lock.acquire()
            self.__read_counter = size
            self.__read_counter_lock.release()
            self.__read_counter_exceeded_event.clear()
            self.__read_counter_exceeded_event.wait()
        self.__write_lock.acquire()
        if self.__echo_enabled:
            self.__history += self.__inp_buf
        read_val = self.__inp_buf

        self.__put_cursor(0)
        self.__redraw_pad()
        self.__write_lock.release()
        self.__read_lock.release()
        return self.__main.str_to_external(read_val)


    def readline(self, size=None):
        """
        Читает одну строку из входного потока. (До первого \n)
        """
        self.__read_lock.acquire()
        self.__newline_event.clear()
        self.__newline_event.wait()
        self.__write_lock.acquire()
        if self.__echo_enabled:
            self.__history += self.__inp_buf
        read_val = self.__inp_buf
        self.__inp_buf = u""
        self.__put_cursor(0)
        self.__redraw_pad()
        self.__write_lock.release()
        self.__read_lock.release()
        print >>self, "\n",
        #self.__put_cursor(0)
        return self.__main.str_to_external(read_val)

    def seek(self, offset, whence=0):
        """
        Перемещается на заданную позицию в файловом потоке (не реализовано)
        Подробнее: http://docs.python.org/library/stdtypes.html#bltin-file-objects
        Аргументы:
        offset -
        whence -
        """
        raise IOError("Not implemented")

    def tell(self):
        """
        Возращает позицию в файловом потоке (не реализовано)
        Подробнее: http://docs.python.org/library/stdtypes.html#bltin-file-objects
        """
        raise IOError("Not implemented")

    def truncate(self, size=None):
        """
        Уменьшает размер файла (путём отрезания "хвоста") (не реализовано)
        Подробнее: http://docs.python.org/library/stdtypes.html#bltin-file-objects

        Аргументы:
        size - число байт
        """
        raise IOError("Not implemented")

    def write(self, str, hist=True):
        """
        Записывает строку str в выходной поток.

        Аргументы:
        str - записываемая строка
        hist - записывать ли строку в историю консоли (по умолчанию - True)
        """

        str = self.__main.str_to_internal(str)
        self.__write_lock.acquire()
        if hist:
            self.__history += self.__main.str_to_internal(str)
        self.__scrolldown()
        self.__redraw_pad()
        self.__write_lock.release()

    def writelines(self, sequence):
        """
        Записывает последовательность строк в файловый поток

        Аргументы:
        sequence - последовательность строк
        """
        for s in sequence:
            self.write(s)

class RunOneFunctionThread(threading.Thread):
    """
    Класс-поток, запускающий заданную в конструкторе функцию в отдельном потоке
    """
    def __init__(self, func, args_list=[], args_dict={}):
        """
        Конструктор.

        Аргументы:
        func - запускаемая функция
        args - аргументы
        """
        self.__func = func
        self.__args_list = args_list
        self.__args_dict = args_dict
        self.__returning_lock = threading.Lock()
        self.__returning_lock.acquire()
        self.__retval = None
        threading.Thread.__init__(self)

    def run(self):
        self.__retval = self.__func(*self.__args_list, **self.__args_dict)
        self.__returning_lock.release()
        
    def get_retval(self):
        self.__returning_lock.acquire()
        self.__returning_lock.release()
        return self.__retval

class ConsolePanelMercurialUI(ui.ui):
    """
    Класс, наследуемый от mercurial.ui.ui.
    Переопределяются некоторые методы для того, чтобы корректно работать с
    ConsolePanel, которая не является полноценным терминалом
    """
    def __init__(self, src=None):
        self.console_panel = None
        self.main_window = None
        self.main = None
        self.commit_message = ""
        ui.ui.__init__(self, src)

    def copy(self):
        c = self.__class__(self)
        c.console_panel = self.console_panel
        c.main_window = self.main_window
        c.main = self.main
        c.commit_message = self.commit_message
        return c

    def getpass(self, prompt=None, default=None):
        if not self.interactive():
            return default
        try:
            self.fout.write(prompt or _('password: '))
            self.console_panel.set_echo(False)
            passw = self.fin.readline()
            self.console_panel.set_echo(True)
            return passw
        except EOFError:
            raise util.Abort(_('response expected'))
        

    #def edit(self, text, user):
        #fin_old = self.fin
        #fout_old = self.fout
        #ferr_old = self.ferr
        #self.fin = sys.stdin
        #self.fout = sys.stdout
        #self.ferr = sys.stderr
        #self.main.hide()
        #txt = ui.ui.edit(self, text, user)
        #self.main.unhide()
        #self.fin = fin_old
        #self.fout = fout_old
        #self.ferr = ferr_old

        #return txt

    def edit(self, text, user):
        #if text is None:
            #text = ''
        #text = self.__main.str_to_internal(self.commit_message + text)
        #focus_on_OK = False
        #if self.commit_message != "":
            #focus_on_OK = True
        #position = len(self.commit_message)
        #editor = CommitWindow(text, focus_on_OK, position)
        #res = self.main.show_modal_window(editor)
        def invoke_editor(text, commit_message, main):
            if text is None:
                text = ''
            text = self.main.str_to_internal(commit_message + text)
            focus_on_OK = False
            if commit_message != "":
                focus_on_OK = True
            position = len(commit_message)
            editor = CommitWindow(text, focus_on_OK, position)
            res = main.show_modal_window(editor)
            return res
        invoke_editor_runnable = RunOneFunctionThread(invoke_editor, 
            [text, self.commit_message, self.main])
        self.main.invoke_later(invoke_editor_runnable)
        res = invoke_editor_runnable.get_retval()
        if res[u'button'] == u'OK':
            return res[u'message']
        else:
            return ''
